<?php
namespace DRY\Module;

/**
 * DonReY Framework 2012 :: Standardized storage of object under a Key-Value system, using various storage engines
 * This is DonReY's implementation of the NoSQL concept ( http://en.wikipedia.org/wiki/NoSQL )
 * Can be used for data caching, or synching between threads, or various other purposes that need the simple concept of accessing an object by it's key.
 * Possible storage engines:
 * - Memcached ( uses Memcache PHP extension ) - Non persistent, Limited storage space
 * - APC ( uses APC PHP extension ) - Only available while until server restart, Limited storage space
 * - File - one file per key, optionally distributed in a hash tree ( to limit the number of files per directory ), Persistent, Possibly limited number of keys
 * - SQLite3 - Stores keys inside one or more SQLite3 databases, Persistent
 * - Database - Stores key using a standard SQL database connector (database.module), Persistent (slower)
 *
 * @author Wavetrex <wavetrex@gmail.com>
 * @link http://www.donrey.net/Documentation/Structure/Modules/nosql.html
 * @license [GPL v3] http://www.gnu.org/licenses/gpl-3.0.html
 * @version 1.0.dev
 * @package DonReY
 * @subpackage Module.NoSQL
 */
class NoSQL extends base
{
	const
		Autoload_Directory = 'nosql',
		Autoload_Prefix = 'DRY\NoSQL\Storage',
		Autoload_Suffix = 'storage'
		;

	/**
	 * Key-Value Storage object. All storage objects must respond in the same way indifferent of the method they use to store data!
	 * @var \DRY\NoSQL\Storage
	 */
	private $kvStore;

	/**
	 * Module Constructor
	 * @param string $alias
	 * @param SimpleXMLElement $config
	 */
	public function __construct($alias, $config)
	{
		parent::__construct($alias, $config);

		// initialization of credentials handler/storage object
		$class_name=self::Autoload_Prefix.'_'.$this-> CFG-> storage['type'];
		$this-> kvStore=new $class_name($this-> CFG-> storage, $this);

		if(isset($this-> CFG-> expire))
			$this-> kvStore->_Set_Expire((int)$this-> CFG-> expire);
	}

	public function expire($limit = 0)
	{
		$this-> kvStore->_Set_Expire($limit);
	}

	public function __set($key, $value)
	{
		$this-> kvStore-> set($key, $value); // Todo: Expiry
	}

	public function __get($key)
	{
		return $this-> kvStore-> get($key);
	}

	public function __unset($key) {
		$this-> kvStore-> delete($key);
	}

	public function __isset($key) {
		return $this-> kvStore-> check($key);
	}

	/**
	 * Check/Get/Set combo, which uses a callback to generate data if not found in the storage, then store the data
	 *
	 * @param string $key
	 * @param callable $callback
	 * @param array $param_arr Optional parameters for the callback
	 */
	public function test_Call($key, $callback, $param_arr = array())
	{
		// when data exists in storage, return it
		if($this-> kvStore-> check($key)) {
			return $this-> kvStore-> get($key);
		} else {
		// if not, use the callback
			$data = call_user_func_array($callback, $param_arr);
			$this-> kvStore-> set($key, $data);
			return $data;
		}
	}

	/**
	 * Reads the data then waits for it to change by polling it repeatedly, returns immediatly when the data has changed or when the timeout has expired
	 *
	 * @param string $key
	 * @param float $timeout Microseconds to wait for data change
	 * @param int $poll_interval Microseconds between polls If null, the data is polled at intervals specified in the Storage engine (some might require longer intervals not to be overloaded)
	 */
	public function wait_For_Change($key, $timeout, $poll_interval = null)
	{
		return $this-> kvStore-> wait_For_Change($key, $timeout, $poll_interval);
	}

}

namespace DRY\NoSQL;

/**
 * Mandatory functions for NoSQL's Key-Storage system. Any type of data storage engine must implement these functions
 *
 * @author Wavetrex <wavetrex@gmail.com>
 * @link http://www.donrey.net/Documentation/Structure/Modules/nosql.html
 * @license [GPL v3] http://www.gnu.org/licenses/gpl-3.0.html
 * @version 1.0.dev
 * @package DonReY
 * @subpackage Module.NoSQL
 */
interface Storage
{
	public function set($key, $data);
	public function get($key);
	public function delete($key);
	public function check($key);
	/**
	 * Reads the data, then waits for it to change for a while, and returns the data (with the information if the data has changed or not since the method call)
	 * @param string $key
	 * @param float $timeout Microseconds to wait for data change
	 * @param int $poll_interval Microseconds between polls If null, the data is polled at intervals specified in the Storage engine (some might require longer intervals not to be overloaded)
	 */
	public function wait_For_Change($key, $timeout, $poll_interval = null);
}
